const { ccclass, property } = cc._decorator;
import ParticleConstruct from './Helper/UI/ParticleConstruct'
import DropItemConstruct from './Component/DropItem'
import SoundManager from './Manager/SoundManager'
import ButtonDialog, { PlayStatus } from './component/ButtonDialog'
import { loadSprite } from './helper/Loader/index'
import { setHomeBtn } from './helper/UI/StaticNode'
import { getUserInfo, createUserInfoButton } from './helper/User/login'
/**
 * @description 下落节点数据
 * @author 刘建文
 * @date 2019-10-09
 * @interface TableItem
 */
interface TableItem {
    a: number
    b: number
    sum: number
}
/**
 * @description 下落节点对应任务
 * @author 刘建文
 * @date 2019-10-09
 * @interface DropTask
 */
interface DropTask {
    node: cc.Node
    sign: number
}
/**
 * @description 难度对应任务下落时间枚举
 * @enum {number}
 */
enum TaskScore {
    SpecialFast = 1,
    Fast = 1.5,
    Middle = 2,
    Slow = 2.5
}
/**
 * @description 不同难度需要加的分数
 */
const MapScore = {
    [TaskScore.Slow]: 10,
    [TaskScore.Middle]: 20,
    [TaskScore.Fast]: 30,
    [TaskScore.SpecialFast]: 40
}
@ccclass
export default class MathGame extends cc.Component {
    @property // 定时器
    timer: number = 0
    @property // 全部待消除节点
    allNodes: Array<TableItem> = []
    @property // 任务队列
    taskList: Array<DropTask> = []
    @property
    scoreNode: cc.Node = null
    @property // 中场按钮节点
    finalButton: ButtonDialog = null
    @property // 当前游戏状态
    status: PlayStatus = 0
    @property
    ParticleConstruct: ParticleConstruct = null
    @property // 分数
    score: number = 0
    @property
    DropItem: DropItemConstruct = null
    @property // 运算最大数值
    max: number = 9
    @property
    allCtrl: Array<number> = Array.from({ length: 9 }).map((_, i) => i + 1)
    @property
    controllerStatus = false
    speed: TaskScore = TaskScore.Slow // 节点掉落速度
    socket = null // socket实例
    socketNode: cc.Node = null
    // 任务创建间隔
    createSpeed = 2
    // 控制器节点
    private ctrlNodePool: cc.NodePool = new cc.NodePool()
    fontSize = 20
    opponentScore = 0

    async onLoad() {
        // this.connectSocket()
        // if (CC_WECHATGAME) {
        //     let openDataContext = wx.getOpenDataContext()
        //     openDataContext.postMessage({
        //         text: 'hello',
        //         year: (new Date()).getFullYear()
        //     })
        //     const callback = (res) => {
        //         const req = (userInfo) => {
        //             cc.log(userInfo)
        //             wx.request({
        //                 url: 'http://0.0.0.0:8080/getUninoid',
        //                 method: 'GET',
        //                 data: {
        //                     js_code: wx.getStorageSync('code'),
        //                     encryptedData: userInfo.encryptedData,
        //                     iv: userInfo.iv
        //                 },
        //                 success(res) {
        //                     cc.log(res)
        //                 }
        //             })
        //         }
        //         cc.log('callback')
        //         cc.log(res)
        //         if (res.encryptedData) {
        //             req(res)
        //         } else {
        //             wx.getUserInfo({
        //                 success(userInfo) {
        //                     req(userInfo)
        //                 }
        //             })
        //         }
        //     }
        //     wx.login({
        //         success: res => {
        //             cc.log(res)    //1. 获取usercode作为后面获取openid的参数
        //             wx.setStorageSync('code', res.code);
        //             cc.log(wx.getStorageSync('code'))
        //             wx.getSetting({
        //                 success(setting) {
        //                     if (setting.authSetting['scope.userInfo']) {
        //                         callback(setting)
        //                     } else {
        //                         createUserInfoButton(callback)
        //                     }
        //                 },
        //                 fail(msg) {
        //                     cc.log(msg)
        //                 }
        //             })

        //         }
        //     })
        // }
        setHomeBtn.call(this, this)
        // 加载音乐资源
        this.loadSounds()
        // 修复wxgame中prototype中创建失败
        this.ParticleConstruct = new ParticleConstruct(this)
        // 注册中场按钮节点
        this.finalButton = this.addComponent(ButtonDialog)
        this.finalButton.RegisterEvent({
            0: {
                string: '寻找对手',
                callback: this.sleepInitGame.bind(this)
            },
            1: {
                string: '继续挑战',
                callback: () => {
                    cc.director.loadScene('math')
                }
            },
            // 1: {
            //     string: '真可惜，再试一次吧',
            //     callback: this.initGame.bind(this)
            // },
            2: {
                string: '继续游戏',
                callback: () => { }
            }
        })
        // 下落节点注册
        this.DropItem = new DropItemConstruct({
            context: this.node,
            finishCallback: this.fail.bind(this),
            killedCallback: node => {
                // 添加爆炸效果
                const position = node.position
                const tag = 'boom'
                this.ParticleConstruct.Create(position, tag, 'prefab/boom2')
                this.updateScore(this.score + MapScore[this.speed])
                SoundManager.getInstance().playFx('peng')
                // 使用最新一个任务创建键盘
                const newTask = this.taskList[this.taskList.length - 1]
                console.log('newtask', newTask)
                // 还有后续任务继续创建键盘，否则清空键盘标识(未清除节点)
                if (newTask) {
                    this.updateController(newTask.sign)
                } else {
                    this.controllerStatus = false
                }
                if (this.socket) {
                    const data = {
                        type: 1,
                        message: this.score
                    }
                    this.socket.send(JSON.stringify(data))
                }
                this.socketNode.removeComponent(cc.Label)
                const label = this.socketNode.addComponent(cc.Label)
                this.opponentScore = this.score
                label.string = `神秘玩家:${this.score}`
                label.fontSize = this.fontSize
                const pos = cc.v2()
                if (CC_WECHATGAME) {
                    this.node.convertToNodeSpaceAR(cc.v2(this.node.width - 10, this.node.height - 110), pos)
                } else {
                    this.node.convertToNodeSpaceAR(cc.v2(this.node.width - 10, this.node.height - 60), pos)
                }
                this.socketNode.position = cc.v2(this.node.width / 2 - 20, this.node.height / 2 - 110)
            }
        })
        // 开启碰撞检测
        const manager = cc.director.getCollisionManager()
        manager.enabled = true;
        if (CC_DEBUG) {
            manager.enabledDebugDraw = true;
            cc.log(manager)
        }

        // 添加碰撞节点-终点线
        const finishLine = new cc.Node('finishLine')
        this.node.addChild(finishLine)
        try {
            const res = await loadSprite('image/default_sprite_splash', cc.SpriteFrame)
            const sp = finishLine.addComponent(cc.Sprite)
            sp.spriteFrame = res;
        } catch (error) { }
        finishLine.width = this.node.width
        finishLine.height = 1
        finishLine.color = new cc.Color(255, 0, 0)
        finishLine.group = 'finishLine'

        const position = cc.v2()
        this.node.convertToNodeSpaceAR(cc.v2(this.node.width / 2, 90), position)
        finishLine.position = position
        const collider = finishLine.addComponent(cc.BoxCollider)
        collider.name = "finishLineCollider"
        collider.size = cc.size(finishLine.width, 2)
    }
    /**
     * @description 初始化游戏
     * @author 刘建文
     * @date 2019-10-09
     * @memberof MathGame
     */
    initGame() {
        SoundManager.getInstance().playMusic('math')
        this.status = PlayStatus.Start
        this.score = 0
        this.updateScore()
        setTimeout(() => {
            this.createItem()
            this.schedule(this.createItem, this.createSpeed)
        }, 500);
    }
    // 假装在匹配对手
    async sleepInitGame() {
        const fontsize = this.fontSize
        const node = new cc.Node('fake_match')
        this.node.addChild(node)
        const waitTime = Math.ceil(Math.random() * 3) // 最多等3秒
        let num = 1

        node.removeAllChildren()
        const labelNode = new cc.Node('label')
        node.addChild(labelNode)
        const label = labelNode.addComponent(cc.Label)
        label.fontSize = fontsize
        label.string = `正在匹配玩家`
        cc.log('本次随机' + waitTime)

        const fakeWait = () => {
            node.removeAllChildren()
            const labelNode = new cc.Node('label')
            node.addChild(labelNode)
            const label = labelNode.addComponent(cc.Label)
            label.fontSize = fontsize
            if (num > waitTime) {
                label.string = `准备进入游戏...`
                // 时间到，进入游戏
                this.unschedule(fakeWait)
                setTimeout(() => {
                    // 清除匹配
                    this.node.removeChild(node)
                    this.initGame()
                    // 同步对手分数
                    this.socketNode = new cc.Node('socket_node')
                    this.socketNode.setAnchorPoint(cc.v2(1, 1))
                    this.socketNode.color = cc.color(255, 0, 0)
                    this.node.addChild(this.socketNode)
                    const label = this.socketNode.addComponent(cc.Label)
                    label.string = `神秘玩家:${0}`
                    label.fontSize = this.fontSize

                    // this.socketNode.position = this.node.convertToNodeSpace(cc.v2(this.node.width - 10, this.node.height - 60))
                    this.socketNode.position = cc.v2(this.node.width / 2 - 20, this.node.height / 2 - 110)
                }, 500)
            } else {
                label.string = `正在匹配玩家 ${num} S...`
                num++
            }
        }
        this.schedule(fakeWait, 1, waitTime, 0)
    }
    /**
     * @description 初始化关卡信息
     * @author 刘建文
     * @date 2019-10-09
     * @memberof MathGame
     */
    updateScore(score?: number) {
        this.score = score || this.score
        cc.log(this.score)
        // 初始化分数
        if (!this.scoreNode) {
            this.scoreNode = new cc.Node('scoreInfo')
            this.scoreNode.color = cc.color(4, 190, 2)
            this.node.addChild(this.scoreNode)
            // 设置锚点为右上角
            this.scoreNode.setAnchorPoint(cc.v2(1, 1))
            // const position = cc.v2()
            // if (CC_WECHATGAME) {
            //     this.node.convertToNodeSpaceAR(cc.v2(this.node.width - 10, this.node.height - 60), position)
            // } else {
            //     this.node.convertToNodeSpaceAR(cc.v2(this.node.width - 10, this.node.height - 10), position)
            // }
            // this.node.convertToNodeSpaceAR(cc.v2(this.node.width - 10, this.node.height - 10), position)
            this.scoreNode.position = cc.v2(this.node.width / 2 - 10, this.node.height / 2 - 80)
            // this.scoreNode.position = cc.v2(,0)
        } else {
            // 更新分数
            if (this.scoreNode.getComponent(cc.Label)) {
                this.scoreNode.removeComponent(cc.Label)
            }
        }
        const label = this.scoreNode.addComponent(cc.Label)
        label.string = `您的分数:${this.score}分`
        label.fontSize = this.fontSize
        label.lineHeight = 30
        // // 开启缓存
        label.cacheMode = cc.Label.CacheMode.CHAR
    }
    /**
     * @description 初始化所有下落节点
     * @author 刘建文
     * @date 2019-10-09
     * @memberof MathGame
     */
    initNodes() {
        // 添加所有节点
        for (let a = 1; a <= this.max; a++) {
            for (let b = a; b <= this.max; b++) {
                this.allNodes.push({ a, b, sum: a * b })
            }
        }
        if (CC_DEBUG) {
            cc.log(`下落节点创建完成，本次创建${this.allNodes.length}个节点`)
        }
    }
    /**
     * @description 初始化控制器
     * @author 刘建文
     * @date 2019-10-09
     * @memberof MathGame
     */
    updateController(num?: number) {
        // 可视区域尺寸
        const visibleSize = cc.view.getVisibleSize()
        // 输出控制器
        let ctrl = Array.from({ length: 5 }).map(() => null)
        // 控制器按钮宽高
        const buttonHeight = 40
        const buttonWidth = visibleSize.width / ctrl.length
        // 随机roll5个,包括待消除的数字
        // 输出控制器
        ctrl[0] = num
        while (ctrl.includes(null)) {
            const index = Math.floor(Math.random() * this.allCtrl.length)
            if (!ctrl.includes(this.allCtrl[index])) {
                ctrl[ctrl.indexOf(null)] = this.allCtrl[index]
            }
        }
        ctrl = ctrl.sort((a, b) => a - b)
        this.node.children.forEach(el => {
            if (el.name.match(/controller_/)) {
                el.destroy()
            }
        })
        for (let i = 0; i < ctrl.length; i++) {
            // 创建节点并添加
            let node
            if (this.ctrlNodePool.size() > 0) {
                node = this.ctrlNodePool.get()
                node.name = `controller_${ctrl[i]}`
            } else {
                node = new cc.Node(`controller_${ctrl[i]}`)
            }
            this.node.addChild(node)
            node.width = buttonWidth - 10
            node.height = buttonHeight
            // 添加按钮节点
            const btnNode = new cc.Node('button')
            node.addChild(btnNode)
            const btncomponent = btnNode.addComponent(cc.Button)
            btncomponent.transition = cc.Button.Transition.COLOR
            node.on(cc.Node.EventType.TOUCH_START, this.onTouchBegin.bind(this))
            node.on(cc.Node.EventType.TOUCH_END, this.onTouchEnd.bind(this))
            // 添加背景sprite节点
            const backgroundNode = new cc.Node('sprite')
            btnNode.addChild(backgroundNode)
            cc.loader.loadRes('image/btn_normal', cc.SpriteFrame, (err, SpriteFrame) => {
                var sp = backgroundNode.addComponent(cc.Sprite)
                // 使用完成后自动释放
                cc.loader.setAutoReleaseRecursively(SpriteFrame, true);
                sp.spriteFrame = SpriteFrame;
                sp.type = cc.Sprite.Type.SLICED
                backgroundNode.setContentSize(cc.size(node.width, node.height))
            })
            // 按钮添加文字
            const labelNode = new cc.Node('label')
            backgroundNode.addChild(labelNode)
            const label = labelNode.addComponent(cc.Label)
            label.string = `${ctrl[i]}`
            label.node.color = cc.color(255, 255, 255)
            let p = cc.v2()
            node.convertToNodeSpaceAR(cc.v3(Math.floor((this.node.width * i / ctrl.length + (node.width / 2)) + 5), 40 + node.height / 2, 0), p)
            node.position = p
        }
        this.controllerStatus = true
    }
    private onTouchBegin(event: cc.Touch) {
        //获取当前点击的全局坐标
        let temp = event.getLocation();
        //获取当前点击的局部坐标
        let tempClick = this.node.convertToNodeSpaceAR(temp)
        const tag = 'clickNode'
        cc.log(this.ParticleConstruct)
        this.ParticleConstruct.Create(tempClick, tag, "prefab/controller_click")
        SoundManager.getInstance().playFx('bu')
    }
    private onTouchEnd(e: cc.Component.EventHandler) {
        const pressBtnIndex = Number(e.target.name.replace(/controller_/, ''))
        if (this.taskList.length === 0) return
        // 匹配成功，杀死对应下落节点
        if (this.taskList[0].sign === Number(pressBtnIndex)) {
            const killedTask = this.taskList.shift()
            this.DropItem.Kill(killedTask.node)
        }
    }

    /**
     * @description 创建下落节点
     * @author 刘建文
     * @date 2019-10-08
     * @returns 
     * @memberof MathGame
     */
    async createItem() {
        cc.log('createItem')
        if (this.allNodes.length === 0) {
            // 所有运算节点用完之后，再新建一批
            this.initNodes()
        }
        const index = Math.floor(Math.random() * this.allNodes.length)
        const task = this.allNodes.splice(index, 1)[0]
        if (!this.controllerStatus) {
            this.updateController(task.b)
        }
        // 动态计算任务下落速度，积分越高下落越快
        if (this.score < 100) {
            this.speed = TaskScore.Slow
        } else if (this.score < 200) {
            this.speed = TaskScore.Middle
        } else if (this.score <= 300) {
            this.speed = TaskScore.Fast
        } else {
            this.speed = TaskScore.SpecialFast
        }
        cc.log('准备创建节点')
        const { a, b, sum } = task
        this.DropItem.Create(this.speed, { a, b, sum }).then(node => {
            this.taskList.push({ sign: b, node })
            if (!this.controllerStatus) {
                this.updateController(this.taskList[0].sign)
            }
        })
    }
    /**
     * @description 游戏失败调用
     * @author 刘建文
     * @date 2019-10-09
     * @memberof MathGame
     */
    fail() {
        this.resetGame(PlayStatus.Fail)
        let msg = `您的分数 ${this.score},对手分数 ${this.opponentScore}`
        this.finalButton.SetStatus(PlayStatus.Fail, msg)
        SoundManager.getInstance().stopMusic()
        this.score = 0
        this.controllerStatus = false
    }
    /**
     * @description 重置游戏状态
     * @author 刘建文
     * @date 2019-10-09
     * @param {number} status
     * @memberof MathGame
     */
    resetGame(status: PlayStatus) {
        // 判断是否进入下一关
        this.status = status
        // 清除定时任务
        this.unschedule(this.createItem)
        // 清除所有任务
        while (this.taskList.length !== 0) {
            const task = this.taskList.pop()
            task.node.destroy()
        }
    }
    connectSocket() {
        this.socket = new WebSocket("ws://60.205.189.44:9506");

        this.socket.onopen = (event) => {  //绑定连接事件
            console.log("Connection open ...");
        };

        this.socket.onmessage = event => {//绑定收到消息事件
            const { type, message } = JSON.parse(event.data)
            cc.log('receive', type, message, event.data)
            if (type === 1) {
                this.socketNode.removeComponent(cc.Label)
                const label = this.socketNode.addComponent(cc.Label)
                this.opponentScore = message
                label.string = `神秘玩家:${message}`
                label.fontSize = this.fontSize
                const position = cc.v2()
                // if (CC_WECHATGAME) {
                //     this.node.convertToNodeSpaceAR(cc.v2(this.node.width - 10, this.node.height - 110), position)
                // } else {
                //     this.node.convertToNodeSpaceAR(cc.v2(this.node.width - 10, this.node.height - 60), position)
                // }
                this.socketNode.position = cc.v2(this.node.width / 2 - 20, this.node.height / 2 - 110)
            }
        };

        this.socket.onclose = event => { //绑定关闭或断开连接事件
            console.log("Connection closed.");
        };
    }
    /**
     * @description 加载音频资源
     * @author 刘建文
     * @date 2019-10-12
     * @memberof MathGame
     */
    loadSounds() {
        cc.loader.loadResDir('sound', cc.AudioClip, (err, clips) => {
            for (let i = 0; i < clips.length; i++) {
                SoundManager.getInstance().addSound(clips[i].name, clips[i]);
            }
        });
    }
}
